#include "stdafx.h"
#include "framework.h"

#include "camera.h"
#include "object.h"
#include "mesh.h"
#include "terrain.h"
#include "objectmanager.h"

CFramework::CFramework()
{
	m_hInstance = NULL;
	m_hWnd= NULL;
	m_hMenu= NULL;

	m_pD3D= NULL;
	m_pD3DDevice= NULL;

	m_bWindowed = true;
	m_bLostDevice = false;
	m_bActive = true;

	m_nViewX;
	m_nViewY;
	m_nViewWidth = FULLSCREEN_WIDTH;
	m_nViewHeight = FULLSCREEN_HEIGHT;

	m_pBox = NULL;
	m_pObject = NULL;
	m_pCamera = NULL;
	m_pTerrain = NULL;

	m_ptOldCursorPos.x = 0;    
	m_ptOldCursorPos.y = 0;    
	
	m_nLights = 4;

	m_pObjectMgr = new CObjectManager;
}

CFramework::~CFramework()
{

}

bool CFramework::Create(HINSTANCE hInstance, HWND hMainWindow)
{
	m_hInstance = hInstance;
	m_hWnd = hMainWindow;

	if( !(m_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) ) return false;

	if( !(CreateDirect3DDisplay()) ) return false;

	if( !(BuildObjects()) ) return false;

	SetUpGameState();
	SetUpRenderStates();
	
	return true;
}

D3DPRESENT_PARAMETERS CFramework::BuildPresentParameters()
{
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));

	d3dpp.BackBufferCount = 1;
	d3dpp.BackBufferWidth = (m_bWindowed) ? m_nViewWidth : FULLSCREEN_WIDTH ;
	d3dpp.BackBufferHeight = (m_bWindowed) ? m_nViewHeight : FULLSCREEN_HEIGHT;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
	d3dpp.MultiSampleQuality = 0;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = m_hWnd;
	d3dpp.Windowed = m_bWindowed;
	d3dpp.EnableAutoDepthStencil = true;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
	d3dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
	d3dpp.FullScreen_RefreshRateInHz = (m_bWindowed) ? 0: 60;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;

	return d3dpp;
}

void CFramework::ChangeWindowProperties()
{
	if( m_bWindowed )
	{
		UINT uFlags = SWP_NOACTIVATE | SWP_SHOWWINDOW;
		SetWindowLong(m_hWnd,GWL_STYLE, WS_OVERLAPPEDWINDOW);
		RECT rc;
		GetWindowRect(m_hWnd,&rc);
		SetWindowPos(m_hWnd,HWND_NOTOPMOST, rc.left , rc.top, rc.right - rc.left, rc.bottom - rc.top, uFlags);
		if( m_hMenu) SetMenu(m_hWnd,m_hMenu);
	}
	else
	{
		m_hMenu = GetMenu(m_hWnd);
		SetMenu(m_hWnd,NULL);
		SetWindowLong(m_hWnd , GWL_STYLE , WS_VISIBLE | WS_POPUP);
		SetWindowPos(m_hWnd , NULL, 0 , 0 ,m_nViewWidth,m_nViewHeight, SWP_NOZORDER);
	}

}

bool CFramework::CreateDirect3DDisplay()
{
	if( m_hWnd) ChangeWindowProperties();

	if( m_pD3DDevice) m_pD3DDevice->Release();
	m_pD3DDevice = NULL;

	D3DPRESENT_PARAMETERS d3dpp = BuildPresentParameters();
	HRESULT hResult = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL, m_hWnd,D3DCREATE_HARDWARE_VERTEXPROCESSING| D3DCREATE_PUREDEVICE,&d3dpp,&m_pD3DDevice);
	if( FAILED(hResult)) return false;

	RECT rc;
	GetClientRect(m_hWnd, &rc);
	m_nViewWidth = rc.right - rc.left;
	m_nViewHeight = rc.bottom - rc.top;

	m_pCamera = new CCamera;
	
	m_pCamera->SetViewport(0,0,m_nViewWidth, m_nViewHeight, 0.0f , 1.0f);
	m_pCamera->SetViewport(m_pD3DDevice);
	
	m_pCamera->SetProjectionProperties(1.01f,50000.0f, D3DXToRadian(60.0f));
	m_pCamera->UpdateProjectionMatrix( m_pD3DDevice);

	m_pCamera->SetOffSet( D3DXVECTOR3(0,2000,0) );
	m_pCamera->SetPivot( D3DXVECTOR3(0,0,0));
	m_pCamera->SetPosition( D3DXVECTOR3(0,2000,0) );				// pos=pivot+ offset
	
//	m_pCamera->SetLookAtPoint( D3DXVECTOR3(0,0,0));
	m_pCamera->Rotate(90,0,0);
	m_pCamera->UpdateViewMatrix( m_pD3DDevice);

	return true;
}

void CFramework::ResetDisplay(bool bChangeDevice, int nClientWidth, int nClientHeight)
{
	if( m_pD3DDevice)
	{
		if( bChangeDevice ) m_bWindowed = !m_bWindowed;
		if( m_bWindowed && (nClientWidth>0) && (nClientHeight>0) )
		{
			m_nViewWidth = nClientWidth;
			m_nViewHeight = nClientHeight;
		}
		
		if(m_hWnd) ChangeWindowProperties();

		D3DPRESENT_PARAMETERS d3dpp = BuildPresentParameters();
		HRESULT hResult = m_pD3DDevice->Reset(&d3dpp);
		SetUpRenderStates();
	}
}

void CFramework::SetUpGameState()
{

}

void CFramework::SetUpRenderStates()
{
	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE);
	m_pD3DDevice->SetRenderState( D3DRS_DITHERENABLE, TRUE);
	m_pD3DDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
	m_pD3DDevice->SetRenderState( D3DRS_CULLMODE , D3DCULL_CCW );

	m_pD3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	m_pD3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	m_pD3DDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
	m_pD3DDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	m_pD3DDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	m_pD3DDevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

	m_pD3DDevice->SetRenderState( D3DRS_LIGHTING, TRUE);
	m_pD3DDevice->SetRenderState(D3DRS_AMBIENT, 0xff101010);

	
	m_ppd3dLights = new D3DLIGHT9*[m_nLights];
	for(int i = 0 ; i < m_nLights; i++)
	{
		m_ppd3dLights[i] = new D3DLIGHT9;
		ZeroMemory( m_ppd3dLights[i], sizeof( D3DLIGHT9));
	}
	m_ppd3dLights[0]->Type = D3DLIGHT_DIRECTIONAL;
    m_ppd3dLights[0]->Diffuse = D3DXCOLOR(1.0f, 0.92f, 0.82f, 0.0f);
    m_ppd3dLights[0]->Specular = D3DXCOLOR(1.0f, 0.92f, 0.82f, 0.0f);
    m_ppd3dLights[0]->Direction = D3DXVECTOR3(0.819f, -0.573f, 0.0f);
    
	m_ppd3dLights[1]->Type = D3DLIGHT_DIRECTIONAL;
    m_ppd3dLights[1]->Diffuse = D3DXCOLOR(0.4f, 0.4f, 0.4f, 0.0f);
    m_ppd3dLights[1]->Specular = D3DXCOLOR(0.6f, 0.6f, 0.6f, 0.0f);
    m_ppd3dLights[1]->Direction = D3DXVECTOR3(-0.819f, -0.573f, -0.0f);
    
	m_ppd3dLights[2]->Type = D3DLIGHT_DIRECTIONAL;
    m_ppd3dLights[2]->Diffuse = D3DXCOLOR(0.8f, 0.8f, 0.8f, 0.0f);
    m_ppd3dLights[2]->Specular = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f);
    m_ppd3dLights[2]->Direction = D3DXVECTOR3(0.0f, 0.707107f, -0.707107f);
    
	m_ppd3dLights[3]->Type = D3DLIGHT_DIRECTIONAL;
    m_ppd3dLights[3]->Diffuse = D3DXCOLOR(0.6f, 0.6f, 0.6f, 0.0f);
    m_ppd3dLights[3]->Specular = D3DXCOLOR(0.6f, 0.6f, 0.6f, 0.0f);
    m_ppd3dLights[3]->Direction = D3DXVECTOR3(0.0f, 0.707107f, 0.707107f);
	
	for(int i = 0 ; i < m_nLights; i++)
	{
		m_pD3DDevice->SetLight(i,m_ppd3dLights[i]);
		m_pD3DDevice->LightEnable(i, true);
	}
	
}

bool CFramework::BuildObjects()
{
	CMesh *pMesh = new CMesh(36, D3DFVF_XYZ | D3DFVF_DIFFUSE);    
	pMesh->Build();

	m_pBox = new CObject(_T("Box"),1,pMesh);
	m_pBox->SetPosition( D3DXVECTOR3(5.5f, 850.0f, 5.0f));
	
	//CXMesh* pXMesh = new CXMesh();
	//pXMesh->LoadFromXFile( _T("SpaceShip.x"),D3DXMESH_MANAGED,m_pD3DDevice);				
	//m_pObject = new CObject(_T("Object"),2,pXMesh);

	//m_pObject->SetScale(0.2f);
	//m_pObject->SetPosition( D3DXVECTOR3(-5.5f, 500.0f, -5.0f));
	//m_pObject->SetChildFrame(m_pBox);
	//m_pBox->SetInherit(false);

	m_pTerrain = new CTerrain(257,257);
	m_pTerrain->Build(m_pD3DDevice);

	return true;
}

void CFramework::ReleaseObjects()
{
	//if(m_pObject )
	//	delete m_pObject;
	//m_pObject = NULL;

	//if(m_pBox )
	//	delete m_pBox;
	//m_pBox = NULL;

	if( m_pObjectMgr)
		delete m_pObjectMgr;
	m_pObjectMgr = NULL;

	if( m_pTerrain)
		delete m_pTerrain;
	m_pTerrain = NULL;

	for (int i = 0; i < m_nLights; i++)
		if (m_ppd3dLights[i]) delete m_ppd3dLights[i];
	delete []m_ppd3dLights;
	m_ppd3dLights = NULL;
}

void CFramework::Destroy()
{
	ReleaseObjects();

	if(m_pCamera)			delete m_pCamera;

	if(m_pD3D)				m_pD3D->Release();
	
	if(m_pD3DDevice)		m_pD3DDevice->Release();
	
	if(m_hMenu)				DestroyMenu(m_hMenu);
	m_hMenu = NULL;
	SetMenu(m_hWnd,NULL);
	
	if(m_hWnd)				DestroyWindow(m_hWnd);
	m_hWnd = NULL;

}

void CFramework::ProcessInput()
{
	static UCHAR pKeyBuffer[256];
	POINT ptCursorPos;
	float cxDelta = 0.0f, cyDelta = 0.0f;
	
	if (GetKeyboardState(pKeyBuffer))
	{
		if( GetKeyState(VK_LEFT) & 0xFF00 ) 
			m_pCamera->MoveLeft(10);
		if( GetKeyState(VK_RIGHT) & 0xFF00 )
			m_pCamera->MoveRight(10);
		if( GetKeyState(VK_UP) & 0xFF00 )
			m_pCamera->MoveForward(10);
		if( GetKeyState(VK_DOWN) & 0xFF00 )
			m_pCamera->MoveBackward(10);
	}
	if (GetCapture()== m_hWnd)
	{
		SetCursor(NULL);
		GetCursorPos(&ptCursorPos);
		cxDelta = (float)(ptCursorPos.x - m_ptOldCursorPos.x) / 3.0f;
		cyDelta = (float)(ptCursorPos.y - m_ptOldCursorPos.y) / 3.0f;
		SetCursorPos(m_ptOldCursorPos.x, m_ptOldCursorPos.y);
	} 

	if ( (cxDelta != 0.0f) || (cyDelta != 0.0f))
	{
		if (cxDelta || cyDelta) 
		{
			if (pKeyBuffer[VK_LBUTTON] & 0xF0) 
				m_pCamera->Rotate( cyDelta/100, cxDelta/100,0);
				//m_pCamera->RotateOffset(cyDelta,cxDelta,0);								
		}
	} 

}

void CFramework::Processkeyboard(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
	case WM_KEYDOWN:
		break;
	case WM_KEYUP:
		break;

	}
}
void CFramework::ProcessMouse(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int nMouseX;
	int nMouseY;
	switch(message)
	{
	case WM_LBUTTONDOWN:
		nMouseX = LOWORD(lParam);
		nMouseY = HIWORD(lParam);

		SetCapture(hWnd);			
		GetCursorPos(&m_ptOldCursorPos);
		m_pCamera->CheckRayCollision(nMouseX,nMouseY);

		break;
	case WM_LBUTTONUP:
		ReleaseCapture();
		break;
	case WM_RBUTTONDOWN:
		break;
	case WM_RBUTTONUP:
		break;
	case WM_MOUSEMOVE:
		break;
	}
}


void CFramework::AnimateObjects()
{
//	m_pObject->Rotate( 1.5f/ 50.0f , 2.5f/50.0f , 3.5f/50.0f );
	m_pBox->Rotate( 3.5f/ 50.0f , 2.5f/50.0f , 1.5f/50.0f );
}


void CFramework::FrameAdvance()
{
	if( !m_bActive || !m_pD3DDevice) return;
	if(m_bLostDevice)
	{
		HRESULT hResult = m_pD3DDevice->TestCooperativeLevel();
		if(hResult == D3DERR_DEVICENOTRESET)
		{
			ResetDisplay(false,0,0);
			m_bLostDevice = false;
		}
		else
			return ;
	}

	ProcessInput();
	AnimateObjects();

	Render();

}

void CFramework::Render()
{
	m_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER , D3DCOLOR_XRGB(0,0,255),1.0f,0);
	m_pCamera->UpdateViewMatrix( m_pD3DDevice);

//	m_pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME /*: D3DFILL_SOLID */);

	if( SUCCEEDED(m_pD3DDevice->BeginScene()) )
	{
		m_pCamera->CalculateFrustumPlane();

		m_pObjectMgr->ObjectRender(m_pD3DDevice,m_pCamera);
//		m_pObject->Render( m_pD3DDevice,m_pCamera);
		m_pTerrain->Render(m_pD3DDevice,m_pCamera);

//		m_pBox->Render(m_pD3DDevice,m_pCamera);

		m_pD3DDevice->EndScene();
	}

	if( FAILED(m_pD3DDevice->Present(NULL,NULL,NULL,NULL)))
		m_bLostDevice = true;

}













